// 从vue库中导入ref、onMounted、onBeforeMount、watch、computed这些函数，用于创建响应式数据、生命周期钩子、监听数据变化、计算属性等功能
import { ref, onMounted, onBeforeMount, watch, computed } from 'vue';
// 从vue库中导入Ref类型，用于定义响应式数据的类型
import type { Ref } from 'vue';

// 定义一个接口Config，用于描述虚拟列表的配置参数
interface Config {
    // 数据，是一个响应式的数组，数组中元素的类型为any
    data: Ref<any[]>;  
    // 数据列表项的高度，是一个数字
    itemHeitht: number; 
    // 每次渲染的条数，是一个数字
    size: number;  
    // 滚动容器的元素选择器，是一个字符串
    scrollContainer: string; 
    // 用于攀升高度的元素选择器，是一个字符串
    actuaHeightContainer: string; 
    // 用于偏移的元素选择器，是一个字符串
    tranllateContainer: string; 
}

// 定义一个类型别名HtmlEIType，表示HTML元素或null
type HtmlEIType = HTMLElement | null;

// 导出一个默认的自定义Hook函数useVirtualList，接收一个Config类型的配置对象
export default function useVirtualList(config: Config) {
    // 用于存储实际高度容器元素，初始值为null
    let actualHeightContainerEl: HtmlEIType = null;
    // 用于存储偏移容器元素，初始值为null
    let tranlateContainerEl: HtmlEIType = null;
    // 用于存储滚动容器元素，初始值为null
    let scrollContainerEl: HtmlEIType = null;

    // 定义缓冲区大小，用于提前渲染一些数据，以减少滚动时的加载闪烁
    const buffer = 5; 
    // 定义一个响应式变量startIndex，用于表示当前开始渲染的数据索引，初始值为0
    const startIndex = ref(0);
    // 定义一个响应式变量endIndex，用于表示当前结束渲染的数据索引，初始值为config.size + buffer，增加了缓冲区
    const endIndex = ref(config.size + buffer); 

    // 定义一个计算属性actualRenderData，用于获取当前实际渲染的数据
    const actualRenderData = computed(() => {
        // 从config.data中截取数据，增加了缓冲区的处理，确保在滚动时数据能平滑过渡
        return config.data.value.slice(
            // 取startIndex.value - buffer和0中的较大值，防止索引越界
            Math.max(0, startIndex.value - buffer), 
            // 取endIndex.value + buffer和config.data.value.length中的较小值，防止索引越界
            Math.min(config.data.value.length, endIndex.value + buffer) 
        );
    });

    // 用于标记是否正在滚动，初始值为false
    let isScrolling = false;
    // 用于记录上一次的滚动位置，初始值为0
    let lastScrollTop = 0;
    // 用于记录上一次的滚动时间，初始值为当前时间
    let lastScrollTime = Date.now();
    // 用于存储滚动事件的定时器ID，初始值为null
    let scrollTimeout: number | null = null;

    // 定义一个函数loadMoreData，用于模拟加载更多数据
    const loadMoreData = () => {
        // 生成新的数据，这里是生成一个包含100个从config.data.value.length + 1开始递增的数字的数组
        const newData = [...new Array(100).fill(0).map((_, i) => config.data.value.length + i + 1)];
        // 将新数据添加到config.data中
        config.data.value = [...config.data.value, ...newData];
    };

    // 定义一个函数handleScroll，用于处理滚动事件
    const handleScroll = (e: Event) => {
        // 清除之前的定时器
        if (scrollTimeout) clearTimeout(scrollTimeout);

        // 设置新的定时器，延迟16ms执行
        scrollTimeout = setTimeout(() => {
            // 如果正在滚动，则直接返回
            if (isScrolling) return;
            // 标记为正在滚动
            isScrolling = true;

            // 使用requestAnimationFrame在浏览器下一次重绘之前执行回调函数
            requestAnimationFrame(() => {
                // 将事件目标转换为HTMLElement类型
                const target = e.target as HTMLElement;
                // 解构获取目标元素的滚动位置、可见高度和总高度
                const { scrollTop, clientHeight, scrollHeight } = target;

                // 计算滚动速度
                const currentTime = Date.now();
                const scrollSpeed = Math.abs(scrollTop - lastScrollTop) / (currentTime - lastScrollTime);
                // 更新上一次的滚动位置
                lastScrollTop = scrollTop;
                // 更新上一次的滚动时间
                lastScrollTime = currentTime;

                // 根据滚动速度动态调整渲染数量，防止滚动过快时数据加载不及时
                const dynamicSize = Math.min(config.size + Math.floor(scrollSpeed * 10), config.data.value.length);

                // 边界控制: 实际触底,且页面真正常渲染数据时，不在触发后序计算，防止触底抖动
                if (scrollHeight <= scrollTop + clientHeight && endIndex.value >= config.data.value.length) {
                    // 标记为不再滚动
                    isScrolling = false;
                    // 加载更多数据
                    loadMoreData(); 
                    return;
                }

                // 通过设置translateY属性，实现偏移效果，让列表看起来在滚动
                tranlateContainerEl!.style.transform = `translateY(${scrollTop}px)`;

                // 计算并更新开始和结束渲染的索引
                startIndex.value = Math.floor(scrollTop / config.itemHeitht);
                endIndex.value = startIndex.value + dynamicSize;

                // 标记为不再滚动
                isScrolling = false;
            });
        }, 16); // 16ms 相当于 60fps
    };

    // 当组件挂载后执行的生命周期钩子函数
    onMounted(() => {
        // 根据选择器获取实际高度容器元素
        actualHeightContainerEl = document.querySelector(config.actuaHeightContainer);
        // 根据选择器获取滚动容器元素
        scrollContainerEl = document.querySelector(config.scrollContainer);
        // 根据选择器获取偏移容器元素
        tranlateContainerEl = document.querySelector(config.tranllateContainer);

        // 给滚动容器元素添加滚动事件监听器，绑定handleScroll函数
        scrollContainerEl?.addEventListener('scroll', handleScroll);
    });

    // 在组件挂载之前执行的生命周期钩子函数
    onBeforeMount(() => {
        // 给滚动容器元素移除滚动事件监听器，防止重复绑定
        scrollContainerEl?.removeEventListener('scroll', handleScroll);
    });

    // 返回一个包含实际渲染数据的对象，供组件使用
    return { actualRenderData };
}